﻿using System;
using System.Reflection;
using Property;
using Property.DelegateBased;

namespace Property.Samples
{
    /// <summary>
    /// Sample interface proxy class.
    /// Loggs property accesses.
    /// </summary>
    /// <typeparam name="TProxiedInterface">Type of proxied interface.</typeparam>
    public class InterfaceProxy<TProxiedInterface>
    {
        /// <summary>
        /// Holding constructed proxy type.
        /// </summary>
        public static readonly Type Type = PropertyProxy.ConstructType<InterfaceProxy<TProxiedInterface>, Implementation>(typeof(TProxiedInterface));

        /// <summary>
        /// Holding delegate, that can be used for creating an instance of proxy implentation. 
        /// </summary>
        public static readonly Func<TProxiedInterface, TProxiedInterface> Wrap = Constructor.Compile<Func<TProxiedInterface, TProxiedInterface>>(Type);

        /// <summary>
        /// Contains wrapped instance.
        /// </summary>
        private TProxiedInterface _instance;

        /// <summary>
        /// Ctor.
        /// </summary>
        /// <param name="instance"></param>
        protected InterfaceProxy(TProxiedInterface instance)
        {
            _instance = instance;
        }
        /// <summary>
        /// Implementation of interface.
        /// Routes calls to upper level and writes messages.
        /// </summary>
        public class Implementation : DefaultImplementation<InterfaceProxy<TProxiedInterface>>
        {
            public override Func<TBaseType, TResult> ImplementGetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                //this code will run once per implementing property
                var callee = Property<TDeclaringType>.GetGetter<TResult>(property.Name);
                var message = String.Format("Getting value of {0}.{1} == {2}", typeof(TDeclaringType).Name, property, "{0}");
                return (pthis) =>
                {//this code will run every time accessing property
                    var result = callee((TDeclaringType)(object)pthis._instance);
                    Console.WriteLine(message, result);
                    return result;
                };
            }
            public override Action<TBaseType, TResult> ImplementSetter<TBaseType, TDeclaringType, TConstructedType, TResult>(PropertyInfo property)
            {
                var callee = Property<TDeclaringType>.GetSetter<TResult>(property.Name);
                var message = String.Format("Setting value of {0}.{1} = {2}", typeof(TDeclaringType).Name, property, "{0}");
                return (pthis, value) =>
                {
                    Console.WriteLine(message, value);
                    callee((TDeclaringType)(object)pthis._instance, value);
                };
            }
        }
    }
}